"""Busca Local Não-Monótona - BUSCA ITERADA"""
"""Permanece iterando e não traz melhoras? Introduzida perturbação dada via parâmetro."""
import time
import random
from functools import reduce
import pandas as pd

def getProximaMaquina(maquinas):
    return max(maquinas, key=lambda it: getMakespan(maquinas, maquinas.index(it)))

# Imprime resultados.
def mostra(maquinas):
    for i in range(len(maquinas)):
        print("Máquina", i + 1, "com makespan", getMakespan(maquinas, i))
        print(maquinas[i])
        print()

def getMakespan(maquinas, i):
    return reduce(lambda a, b: a + b, maquinas[i], 0)

def init():
    rTarefas = [1.5, 2]
    possibMaquina = [10, 20, 50]
    nivelPerturbacoes = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
    salvar = []

    for m in possibMaquina:
        for r in rTarefas:
            for per in nivelPerturbacoes:
                quantidadeTarefas = int(m ** r)
                tarefas = [random.randint(1, 100) for i in range(quantidadeTarefas)]
                maquinas = [[] for i in range(m)]
                maquinas[0] = tarefas
                contador = 1
                inicio = time.time()
                semMelhora = 0

                while semMelhora < 1000:
                    while movimenta(maquinas):
                        contador += 1
                    movimentaTarefaAleatoriamente(maquinas, per)
                    semMelhora += 1
                    contador += 1

                fim = time.time()
                mostra(maquinas)

                maquinaComMaiorMakespan = getProximaMaquina(maquinas)
                indiceMakespan = maquinas.index(maquinaComMaiorMakespan)
                salvar.append({
                    "iteracoes": contador,
                    "maquinas": m,
                    "nTarefas": quantidadeTarefas,
                    "r": r,
                    "%Perturbacao": per,
                    "Makespan": getMakespan(maquinas,indiceMakespan),
                    "tempo": fim - inicio
                })

    print(salvar)
    df = pd.DataFrame(salvar, columns=['iteracoes', 'maquinas', 'nTarefas', 'r', '%Perturbacao', 'Makespan','tempo'])
    print(df)
    df.to_csv('bnmon.csv', encoding='utf-8', index=False)

def movimentaTarefaAleatoriamente(maquinas, per):
    """"Executa perturbações em grau aleatório na estruturação das tarefas e máquinas."""
    maquinaRemover = getProximaMaquina(maquinas)
    tarefasSorteadas = random.sample(maquinaRemover, int(len(maquinaRemover) * per))

    for tarefaMover in tarefasSorteadas:
        for i in range(len(maquinas)):
            maquinaDestino = maquinas[i]

            if maquinas.index(maquinaRemover) == i:
                continue

            if (getMakespan(maquinas, i) + tarefaMover) < getMakespan(maquinas, maquinas.index(maquinaRemover)):
                maquinaRemover.remove(tarefaMover)
                maquinaDestino.append(tarefaMover)
                break

def movimenta(maquinas):
    """Atribui tarefas a máquinas."""
    maquinaRemover = getProximaMaquina(maquinas)
    tarefaMover = maquinaRemover.pop()

    for i in range(len(maquinas)):
        maquinaDestino = maquinas[i]

        if maquinas.index(maquinaRemover) == i:
            continue

        if (getMakespan(maquinas, i) + tarefaMover) < getMakespan(maquinas, maquinas.index(maquinaRemover)) + tarefaMover:
            maquinaDestino.append(tarefaMover)
            return True

    maquinaRemover.append(tarefaMover)
    return False